package badge

import (
	"errors"
	"reflect"
	"time"

	"gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game/stat"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/orm"
)

type BadgeData struct {
	Badges      []SettingBadge
	BadgeMap    map[int]*SettingBadge
	BadgeParent map[int]*SettingBadge

	BadgeList []*Badge

	env        *game.Environment
	badgeModel *orm.Model
}

func GetBadgeData(env *game.Environment, extendType reflect.Type, insideType reflect.Type) (*BadgeData, error) {
	var err error
	data := &BadgeData{env: env}

	if data.badgeModel, data.Badges, data.BadgeMap, err = getBadgeData(env, extendType, insideType); err != nil {
		return nil, err
	}

	badgeProvider := &game.DataProvider{
		Name:     "badge",
		Language: true,
		Array:    true,
		Get: func() (i interface{}, err error) {
			return data.BadgeList, err
		},
	}
	env.RegisterData(badgeProvider)
	// badge 按ParentId合并
	data.BadgeParent = make(map[int]*SettingBadge)
	for key := range data.Badges {
		badge := data.Badges[key]
		if badge.ParentId > 0 {
			continue
		}
		data.BadgeParent[badge.Id] = &badge
	}
	data.BadgeList = make([]*Badge, len(data.Badges))
	for index := range data.Badges {
		badge := data.Badges[index]

		data.BadgeList[index] = NewBadge(badgeProvider, &badge)
		if badge.ParentId == 0 {
			continue
		}
		if _, ok := data.BadgeParent[badge.ParentId]; !ok {
			continue
		}
		data.BadgeParent[badge.ParentId].Append(&badge)
	}

	return data, err
}

func getBadgeData(env *game.Environment, extendType reflect.Type, insideType reflect.Type) (*orm.Model, []SettingBadge, map[int]*SettingBadge, error) {
	var err error

	model, err := env.GetModel(SettingBadgeName)
	if err != nil {
		return nil, nil, nil, err
	}
	var badges []SettingBadge
	_, err = model.Query().Asc("parent_id").Asc("level").Find(&badges)
	if err != nil {
		return nil, nil, nil, err
	}
	badgeMap := make(map[int]*SettingBadge, len(badges))
	for k := range badges {
		if extendType != nil {
			badges[k].Extend = utils.ToType(badges[k].Extend, extendType)
		}
		if insideType != nil {
			badges[k].Inside = utils.ToType(badges[k].Inside, insideType)
		}
		badgeMap[badges[k].Id] = &badges[k]
	}
	return model, badges, badgeMap, nil
}
func (badgeData *BadgeData) GetAllBadge(updateTime *time.Time, language string) ([]*Badge, error) {
	if updateTime != nil {
		badges := make([]*Badge, 0, 5)
		for _, object := range badgeData.BadgeList {
			if object.HasUpdate(updateTime) {
				badges = append(badges, object)
			}
		}
		return badges, nil
	} else {
		return badgeData.BadgeList, nil
	}
}

func (badgeData *BadgeData) ChangeBadge(badges map[int]*UserBadge, data map[string]interface{}) error {
	score := 0
	for _, userBadge := range badges {
		badge, ok := badgeData.BadgeParent[userBadge.Id]
		if !ok {
			continue
		}
		child := badge.GetLevel(userBadge.Level)
		if child == nil {
			continue
		}
		score += child.Score
	}
	data["score"] = score
	data["badges"] = badges
	return nil
}

func (badgeData *BadgeData) ValidBadge(badges map[int]*UserBadge, user *StatInfo, openId string, badgeId int) (bool, error) {
	badge, ok := badgeData.BadgeParent[badgeId]
	if !ok {
		return false, errors.New("徽章不存在")
	}
	badgeList := badge.List()

	level := -1
	flag := false
	if info, ok := badges[badge.Id]; ok {
		level = info.Level
	}
	for _, child := range badgeList {
		if child.Level <= level {
			continue
		}
		if !user.JudgeFactor(child.Factors) {
			continue
		}
		level = child.Level
		flag = true
	}
	if !flag {
		return false, nil
	}
	newBadge := &UserBadge{
		Id:    badge.Id,
		Level: level,
		Date:  time.Now().UTC().String(),
	}
	badges[badge.Id] = newBadge

	badgeData.env.SendEventNotice(openId, []interface{}{"badge", newBadge.Id, newBadge.Level})
	return true, nil
}
func (badgeData *BadgeData) FullBadge(badges map[int]*UserBadge, user *StatInfo, data []*UserBadge) []*UserBadge {
	for _, badge := range badgeData.BadgeParent {
		badgeList := badge.List()
		// 所有子集的factors的标准一致，所以获取最后一个子集作为goal来源
		child := badgeList[len(badgeList)-1]
		goal := user.GoalFactor(child.Factors)
		userBadge, ok := badges[badge.Id]
		if !ok {
			userBadge = &UserBadge{
				Id:    badge.Id,
				Level: 0,
			}
		}
		userBadge.Goal = goal
		data = append(data, userBadge)
	}
	return data
}
func (badgeData *BadgeData) UpdateBadge(badges map[int]*UserBadge, user *StatInfo, openId string) bool {
	flag := false
	for _, badge := range badgeData.BadgeParent {
		if ok, err := badgeData.ValidBadge(badges, user, openId, badge.Id); err != nil {
			return false
		} else if !ok {
			continue
		}
		flag = true
	}
	return flag
}
